import { CACHE_KEY_METADATA, CACHE_TTL_METADATA, CacheInterceptor } from '@nestjs/cache-manager';
import { CallHandler, ExecutionContext, Injectable, StreamableFile } from '@nestjs/common';
import { Observable, of, tap } from 'rxjs';
import { isFunction, isNil } from '@nestjs/common/utils/shared.utils';
import { IHttpLog } from 'src/common/modules/logger/logger.types';
import { Reflector } from '@nestjs/core';
import { LoggerService } from 'src/common/modules/logger/logger.service';
import { IRequest } from 'src/interface/common';
@Injectable()
export class AppHttpCacheInterceptor extends CacheInterceptor {
	constructor(
		cacheManager: any,
		reflector: Reflector,
		private readonly loggerService: LoggerService,
	){
		super(cacheManager,reflector);
	}

	async intercept(
		context: ExecutionContext,
		next: CallHandler,
	): Promise<Observable<any>> {
		const key = this.trackBy(context);
		const ttlValueOrFactory =
			this.reflector.get(CACHE_TTL_METADATA, context.getHandler()) ?? null;
		if (!key) {
			return next.handle();
		}
		try {
			const value = await this.cacheManager.get(key);
			if (!isNil(value)) {
				let req = context.switchToHttp().getRequest();
				const response = context.switchToHttp().getResponse()
				let logParams:IHttpLog={
					trackId:req.trackId,
					user: req.user._id||"",
					url: req.url,
					method: req.method,
					timeMs: Date.now()-req.requestTime,
					headers: req.headers,
					params: req.params,
					body: req.body,
					responseFrom:"redis",
					responseCode:response.statusCode,
					responseData:value
				}
				this.loggerService.writeHttpLog(logParams)
				return of(value);
			}
			const ttl = isFunction(ttlValueOrFactory)
				? await ttlValueOrFactory(context)
				: ttlValueOrFactory;


			return next.handle().pipe(
				tap(async data => {
					let response=await data
					if (response instanceof StreamableFile) {
						return;
					}

					const args = [key, response];
					if (!isNil(ttl)) {
						args.push(false ? ttl : { ttl });
					}

					try {
						await this.cacheManager.set(...args);
					} catch (err) {
						console.error(
							`An error has occurred when inserting "key: ${key}", "value: ${response}"`,
							'CacheInterceptor',
						);
					}
				}),
			);
		} catch {
			return next.handle();
		}
	}
	trackBy(context: ExecutionContext): string | undefined {
		const cacheKeyMeta = this.reflector.get(
			CACHE_KEY_METADATA,
			context.getHandler(),
		);
		const request:IRequest = context.switchToHttp().getRequest();
		const { httpAdapter } = this.httpAdapterHost;
		const isGetRequest = httpAdapter.getRequestMethod(request) === 'GET';
		const excludePaths = [
			// Routes to be excluded
		];
		if (!request.path.startsWith("/app"))return undefined;
		if (!cacheKeyMeta || !isGetRequest || (isGetRequest && excludePaths.includes(httpAdapter.getRequestUrl(request)))) {
			return undefined;
		}
		let cacheKey = `${cacheKeyMeta}`
		if(cacheKey.includes("user_{id}")){
			cacheKey=cacheKey.replace(/{id}/,request.user?._id)
		}
		if(cacheKey.includes("server_{id}")){
			cacheKey=cacheKey.replace(/{id}/,request.headers["server-id"] as string)
		}
		if(cacheKey.includes("role_{id}")){
			cacheKey=cacheKey.replace(/{id}/,request.headers["role-id"] as string)
		}
		cacheKey = `${cacheKey}|${httpAdapter.getRequestUrl(request)}`
		return cacheKey;
	}
}
